#include "usbcommunication.h"
#include <QDebug>
#include <QByteArray>
#include <QTimer>

USBCommunication::USBCommunication(QThread *parent)
    : QThread{parent}
{
    is_connect = false;
    ctx = nullptr;
    hUsbDevice = nullptr;
    // write_buffer = nullptr;
    current_vid = 0;
    current_pid = 0;
}

USBCommunication::~USBCommunication()
{
    // ctx = nullptr;
    work = false;
    usb_device_disconnect();
    if(ctx != nullptr)
    {
        libusb_exit(ctx);
        ctx = nullptr;
    }
    quit();
    wait();
}


// #if (defined Q_OS_LINUX) || (defined Q_OS_MAC)
int USBCommunication::device_hotplug_callback(struct libusb_context *ctx, struct libusb_device *dev, libusb_hotplug_event event, void *userdata)
{
    struct libusb_device_descriptor desc;

    (void)userdata;
    (void)ctx;

    libusb_get_device_descriptor(dev, &desc);
    if(event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
    {
        qInfo() << "hotplug remove" << QString::asprintf("CLASS(0x%x) SUBCLASS(0x%x) iSerialNumber(0x%x)\n",desc.bDeviceClass, desc.bDeviceSubClass, desc.iSerialNumber);
    }
    else if(event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)
    {
        qInfo() << "hotplug arrived" << QString::asprintf("CLASS(0x%x) SUBCLASS(0x%x) iSerialNumber(0x%x)\n",desc.bDeviceClass, desc.bDeviceSubClass, desc.iSerialNumber);
    }

    return 0;
}

void USBCommunication::cbLibusbAsyncRead(libusb_transfer *transfer)
{
    int rc;

    if(transfer->status == LIBUSB_TRANSFER_COMPLETED)
    {
        if(transfer->actual_length > 0)
        {
            //成功接收到数据
            // cmdDeal(transfer);
            USBCommunication *pObj = reinterpret_cast<USBCommunication *>(transfer->user_data);
            pObj->receive_data.clear();
            pObj->receive_data.append((char *)transfer->buffer,transfer->actual_length);
            // qDebug() << "func:" << __func__ << ",thread id:" << QThread::currentThreadId();
            emit pObj->usb_device_receive_new_data(pObj->receive_data);
        }
        //再次提交传输
        rc = libusb_submit_transfer(transfer);
        if(rc < 0)
        {
            qDebug() << libusb_strerror(libusb_error(rc));
            if(transfer->buffer != nullptr)
            {
                delete [] transfer->buffer;
                transfer->buffer = nullptr;
            }
            //提交失败，释放传输结构
            libusb_cancel_transfer(transfer);
            libusb_free_transfer(transfer);
        }
    }
    else if(transfer->status == LIBUSB_TRANSFER_CANCELLED)
    {
        if(transfer->buffer != nullptr)
        {
            delete [] transfer->buffer;
            transfer->buffer = nullptr;
        }
        libusb_cancel_transfer(transfer);
        libusb_free_transfer(transfer);
    }
}

void USBCommunication::cbLibusbAsyncSend(libusb_transfer *transfer)
{
    // int rc;
    usb_send_user *user = (usb_send_user *)transfer->user_data;
    if(transfer->status == LIBUSB_TRANSFER_COMPLETED)
    {
        if(transfer->buffer != nullptr)
        {
            delete [] transfer->buffer;
            transfer->buffer = nullptr;
        }
        libusb_free_transfer(transfer);
        emit user->handle->usb_device_send_finish(USBCommunication::SUCCESS);
    }
    else
    {
        emit user->handle->usb_device_send_finish(USBCommunication::FAILED);
    }
}

USBCommunication::USB_Status USBCommunication::usb_device_start_asynchronous_read(unsigned char endpoint)
{
    int rc;

    if(hUsbDevice == nullptr)
    {
        return HANDLE_IS_NONE;
    }
    uint16_t maxSize = libusb_get_max_packet_size(libusb_get_device(hUsbDevice),endpoint);
    qDebug() << "start read," <<  "endpoint:" << QString("%1").arg(endpoint,0,16) << " max size:" << maxSize;
    transfer_list.clear();
    for(int i=0; i<10; i++)
    {
        unsigned char *buffer = new unsigned char[maxSize];
        if(buffer != nullptr)
        {
            libusb_transfer *transfer = libusb_alloc_transfer(0);
            libusb_fill_bulk_transfer(transfer,hUsbDevice,endpoint,buffer,512,&cbLibusbAsyncRead,this,0);
            rc = libusb_submit_transfer(transfer);
            if(rc == LIBUSB_SUCCESS)
            {
                transfer->user_data = this;
                transfer_list.append(transfer);
            }
            else
            {
                delete [] buffer;
                transfer->buffer = nullptr;
                qDebug() << libusb_strerror(libusb_error(rc));
                if(transfer != nullptr)
                {
                    libusb_cancel_transfer(transfer);
                    libusb_free_transfer(transfer);
                    transfer = nullptr;
                }
                return FAILED;
            }
        }
    }
    return SUCCESS;
}

USBCommunication::USB_Status USBCommunication::usb_device_start_asynchronous_write(unsigned char endpoint,unsigned char *buff,size_t bytes)
{
    int rc = 0 ;

    if(buff == nullptr)
    {
        return FAILED;
    }
    if(hUsbDevice == nullptr)
    {
        return HANDLE_IS_NONE;
    }
    // if(write_buffer != nullptr)
    // {
    //     delete [] write_buffer;
    // }
    qDebug() << "write data,size:" << bytes;
    unsigned char *write_buffer;
    uint16_t maxSize = libusb_get_max_packet_size(libusb_get_device(hUsbDevice),endpoint);
    qDebug() << "endpoint size:" << QString::number(maxSize);
    // uint32_t count;
    uint32_t offset;
    uint32_t send_size;
    if(maxSize == 0)
    {
        return FAILED;
    }
    // count = (bytes + (maxSize - 1)) / maxSize;
    offset = 0;
    while(bytes)
    {
        libusb_transfer *transfer = libusb_alloc_transfer(0);
        send_user.handle = this;
        send_size = bytes > maxSize ? maxSize : bytes;
        write_buffer = new unsigned char[send_size];
        if(write_buffer == nullptr)
        {
            return FAILED;
        }
        ::memcpy(write_buffer,buff + offset,send_size);
        libusb_fill_bulk_transfer(transfer,hUsbDevice,endpoint,write_buffer,send_size,&cbLibusbAsyncSend,&send_user,500);
        rc = libusb_submit_transfer(transfer);
        if(rc < 0)
        {
            qDebug() << libusb_strerror(libusb_error(rc));
            libusb_free_transfer(transfer);
            // delete [] write_buffer;
            return FAILED;
        }
        offset += send_size;
        bytes -= send_size;
        // QThread::msleep(50);
    }
    return SUCCESS;
}

// #endif

USBCommunication::USB_Status USBCommunication::usb_device_connect(void)
{
    int rc;
    uint16_t vid = current_vid;
    uint16_t pid = current_pid;
    if(is_connect == true)
    {
        return SUCCESS;
    }

    kernelDriverDetached = 0;

    hUsbDevice = libusb_open_device_with_vid_pid(ctx,vid,pid);
    if (!hUsbDevice)
    {
        qWarning() << "open device fail," << QString("vid:%1,pid:%2").arg(vid,4,16).arg(pid,4,16);
        // libusb_exit(ctx);
        return FAILED;
    }
    // 检查内核驱动是否连接到了这个设备，如果是，则从内核驱动断开它
    if (libusb_kernel_driver_active(hUsbDevice, 0))
    {
        rc = libusb_detach_kernel_driver(hUsbDevice, 0);
        if (rc == 0)
        {
            kernelDriverDetached = 1;
        }
        else if(rc != LIBUSB_ERROR_NOT_SUPPORTED)
        {
            qWarning() << "detaching kernel driver not supported" << ",libusb_error:" << libusb_strerror(libusb_error(rc));
            libusb_close(hUsbDevice);
            hUsbDevice = nullptr;
            return FAILED;
        }
    }
    // 声明接口0
    rc = libusb_claim_interface(hUsbDevice, interface_number);
    if (rc != 0)
    {
        qWarning() << "claiming interface fialed" << ",interface:" << interface_number << ",libusb_error:" << libusb_strerror(libusb_error(rc));
        libusb_close(hUsbDevice);
        // libusb_exit(ctx);
        hUsbDevice = nullptr;
        return FAILED;
    }

    is_connect = true;
    qDebug() << "usb is connect";
    start();
    // usb_device_start_receive(0x05);
    emit usb_device_is_connect();
    return SUCCESS;
}

USBCommunication::USB_Status USBCommunication::usb_device_disconnect()
{
    work = false;
    if(hUsbDevice != nullptr)
    {
        // 释放接口0
        int rc = libusb_release_interface(hUsbDevice, interface_number);
        if (0 != rc)
        {
            qWarning() << "releasing interface fialed," << ",libusb_error:" << libusb_strerror(libusb_error(rc));
        }
        //如果我们之前从接口＃0分离了内核驱动程序，我们现在就可以了需要再次附加它
        if (kernelDriverDetached)
        {
            libusb_attach_kernel_driver(hUsbDevice, 0);
        }
        libusb_hotplug_deregister_callback(ctx,device_hotplug_handle);
        // 关闭 libusb
        libusb_close(hUsbDevice);
        // libusb_exit(ctx);
        qInfo() << "libusb is closed";
    }
    else
    {
        qWarning() << "hUsbDevice is null";
    }
    is_connect = false;
    hUsbDevice = nullptr;
    device_hotplug_handle = 0;
    usb_device_cancel_receive();
    emit usb_device_is_disconnect();
    return SUCCESS;
}

USBCommunication::USB_Status USBCommunication::usb_device_init(uint16_t vid, uint16_t pid, int _interface_number)
{
    current_vid = vid;
    current_pid = pid;
    interface_number = _interface_number;
    if(ctx != nullptr)
    {
        libusb_exit(ctx);
        ctx = nullptr;
    }
    int rc = libusb_init(&ctx);
    if(rc != 0)
    {
        qWarning() << "libusb init failed," << ",libusb_error:" << libusb_strerror(libusb_error(rc));
        return FAILED;
    }
    return SUCCESS;
}

USBCommunication::USB_Status USBCommunication::usb_device_start_hotplug(void)
{
    uint16_t vid = current_vid;
    uint16_t pid = current_pid;
    int rc = libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG);
    if(rc)
    {
        rc = libusb_hotplug_register_callback(ctx,
                                              LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
                                              LIBUSB_HOTPLUG_NO_FLAGS,
                                              vid, pid,
                                              LIBUSB_HOTPLUG_MATCH_ANY,
                                              device_hotplug_callback,
                                              NULL,
                                              &device_hotplug_handle);
        if (LIBUSB_SUCCESS != rc)
        {
            qWarning() << "registering hotplug failed" << ",libusb_error:" << libusb_strerror(libusb_error(rc));
            // libusb_exit(ctx);
            return FAILED;
        }
        qInfo() << "register hotplug success";
        return SUCCESS;
    }
    qInfo() << "not supported or unimplemented on this platform  ";
    return NOT_SUPPORT;
}

void USBCommunication::run()
{
    struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 10;
    if(hUsbDevice == nullptr)
    {
        return ;
    }
    if(is_connect == false)
    {
        return ;
    }
    work = true;
    while(work)
    {
        libusb_handle_events_timeout(ctx,&tv);
    }
}

USBCommunication::USB_Status USBCommunication::usb_device_transfer(unsigned char endpoint, unsigned char *buffer, size_t bytes, unsigned int timeout)
{
    int rc ;
    int actual_length;
    unsigned char *buff;
    int size;
    buff = (unsigned char *)buffer;
    size = bytes;

    if(hUsbDevice)
    {
        rc = libusb_bulk_transfer(hUsbDevice,endpoint,buff,size,&actual_length,timeout);
        if(rc == LIBUSB_ERROR_TIMEOUT)
        {
            return TIMEOUT;
        }
        if(rc != 0)
        {
            qWarning() << "Send Error:" << ",libusb_error:" << libusb_strerror(libusb_error(rc));
            return OTHER_ERROR;
        }
    }
    else
    {
        qCritical() << "usb handle is null";
        return HANDLE_IS_NONE;
    }
    return SUCCESS;
}

void USBCommunication::usb_device_change(bool is_arrival,uint16_t vid, uint16_t pid, const QString &serial_number)
{
    // struct libusb_device_descriptor desc;

    if((vid != current_vid) || (pid != current_pid))
    {
        return ;
    }

    if(is_arrival)
    {
        qInfo() << "usb device is arrival,vid:" << QString::number(vid,16).toUpper() << ",pid:" << QString::number(pid,16).toUpper() << ",serial number:" << serial_number;
        if(is_connect == false)
        {
            // usb_device_connect();
            QTimer::singleShot(300,this,&USBCommunication::usb_device_connect);
        }
    }
    else
    {
        qInfo() << "usb device is removed,vid:" << QString::number(vid,16).toUpper() << ",pid:" << QString::number(pid,16).toUpper() << ",serial number:" << serial_number;
        // usb_device_disconnect();
        QTimer::singleShot(300,this,&USBCommunication::usb_device_disconnect);
    }
}

void USBCommunication::usb_device_write_data(const QByteArray &data,unsigned char endpoint,unsigned int timeout)
{
    unsigned char *buff;
    int size;
    buff = (unsigned char *)data.data();
    size = data.size();
    endpoint = endpoint & 0x7F;
    usb_device_start_asynchronous_write(endpoint,buff,size);
}

 USBCommunication::USB_Status USBCommunication::usb_device_start_receive(unsigned char endpoint)
{
    endpoint = endpoint | 0x80;
    return usb_device_start_asynchronous_read(endpoint);
}

void USBCommunication::usb_device_cancel_receive()
{
    for(int i=0; i<transfer_list.size(); i++)
    {
        libusb_transfer *transfer = transfer_list.at(i);
        if(transfer != nullptr)
        {
            if(transfer->buffer != nullptr)
            {
                delete [] transfer->buffer;
                transfer->buffer = nullptr;
            }
            libusb_free_transfer(transfer);
        }
    }
    transfer_list.clear();
}

